package com.example.demo.hotload.db;

import com.example.demo.handler.UploadProxy;
import com.example.demo.handler.excel.Company;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xyz.erupt.jpa.dao.EruptDao;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

@Component
public class HibernateUtil {
    
    @Autowired
    private EruptDao eruptDao;

    public void obtainSessionFactory(Class entityClass, List<Company.Content> contents) throws IOException, ClassNotFoundException {
        Class<?> aClass = getClass().getClassLoader().loadClass(entityClass.getName());
//        String[] implementors = sessionFactory.getMetamodel().getImplementors(entityClass.getName());
        SessionFactory sessionFactory = eruptDao.getEntityManager().getEntityManagerFactory().unwrap(SessionFactory.class);
        StandardServiceRegistry serviceRegistry = sessionFactory.getSessionFactoryOptions().getServiceRegistry();
        MetadataSources metadataSources = new MetadataSources(serviceRegistry);
        metadataSources.addAnnotatedClass(aClass);
        Metadata metadata = metadataSources.buildMetadata();
        // 更新数据库Schema,如果不存在就创建表,存在就更新字段,不会影响已有数据
        SchemaExport schemaUpdate = new SchemaExport();
        schemaUpdate.createOnly(EnumSet.of(TargetType.DATABASE), metadata);

        // 写入数据

        // 这里必须由新增的元数据来创建session，原来的session中不包含新增映射关系（TODO 如何添加到原有的session中？）
        Session session = metadata.buildSessionFactory().openSession();
        // 重建sessionFactory或者合并或者为每个实体创建对应的sessionFactory

        Transaction transaction = session.beginTransaction();
        for (Company.Content content : contents) {
            // 使用map对象操作数据库
            try {
                Object o = aClass.newInstance();
                Field[] fields = aClass.getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    Field field = fields[i];
                    field.setAccessible(true);
                    field.set(o,content.values.get(i));
                }
                session.save(o);
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        transaction.commit();
    }

    /******************** 以下 方法只适用于对象是动态加载进JVM的情况******************************/
    /**
     * 获取Session工厂
     * 如果指定的model是动态加载到JVM中的，
     * 那么就更新当前的configuration，
     * 并重新创建一个Session工厂(因为Session工厂是全局单例的，所以创建好之后最好不要改动，容易出现事务安全问题)，
     * 以便能为该对象执行数据库访问操作
     * 该方法如果频繁使用，会增加系统开销
     */
    public void obtainSessionFactory(ByteArrayOutputStream mappingXmlOutputStream) throws IOException {
//        String[] implementors = sessionFactory.getMetamodel().getImplementors(entityClass.getName());
        SessionFactory sessionFactory = eruptDao.getEntityManager().getEntityManagerFactory().unwrap(SessionFactory.class);
        StandardServiceRegistry serviceRegistry = sessionFactory.getSessionFactoryOptions().getServiceRegistry();
        MetadataSources metadataSources = new MetadataSources(serviceRegistry);
        //读取映射文件
        metadataSources.addInputStream(new ByteArrayInputStream(mappingXmlOutputStream.toByteArray()));
//        metadataSources.addAnnotatedClass(entityClass);
        Metadata metadata = metadataSources.buildMetadata();

        // 更新数据库Schema,如果不存在就创建表,存在就更新字段,不会影响已有数据
        SchemaExport schemaUpdate = new SchemaExport();
        schemaUpdate.createOnly(EnumSet.of(TargetType.DATABASE), metadata);

        // TODO: 2024/2/2 需要新增动态生成的映射关系对应的session

    }

    public void generateXmlMapping(String className, String tableName, UploadProxy.CompanyFunction function) throws DocumentException, IOException {
        SAXReader xml = SAXReader.createDefault();
        org.dom4j.Document document = xml.getDocumentFactory().createDocument();
        document.addDocType("hibernate-mapping", "-//Hibernate/Hibernate Mapping DTD 3.0//EN", "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd");
        org.dom4j.Element rootElement = document.addElement("hibernate-mapping");
        org.dom4j.Element classElement = rootElement.addElement("class");
        classElement.addAttribute("entity-name", className);
        classElement.addAttribute("table", tableName.toLowerCase());
        org.dom4j.Element id = classElement.addElement("id");
        id.addAttribute("name", "id");
        id.addAttribute("type", "int");
        id.addAttribute("column", "id");
        org.dom4j.Element generator = id.addElement("generator");
        generator.addAttribute("class", "native");
        for (UploadProxy.CompanyColumn column : function.getColumns()) {
            org.dom4j.Element property = classElement.addElement("property");
            property.addAttribute("name", column.getFieldName());
            property.addAttribute("type", "java.lang.String");
            property.addAttribute("column", column.getFieldName().toLowerCase());
        }
        // 设置输出格式和输出流
        OutputFormat format = OutputFormat.createPrettyPrint();
//        XMLWriter writer = new XMLWriter(Files.newOutputStream(Paths.get("dynamic.hbm.xml")), format);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        XMLWriter writer = new XMLWriter(outputStream, format);
        writer.write(document);// 将文档写入到输出流
        writer.close();

        obtainSessionFactory(outputStream);
    }
}